<template>
  <div id="app">
    <!-- Echarts 图表 -->
    <div ref="ChartRef" class="progressChart"></div>
  </div>
</template>

<script setup>
import * as echarts from 'echarts'
import { ref, onMounted, watch } from 'vue'

const isYear = ref(true)
const currentMonth = ref(1)
const ChartRef = ref()
let progressChart

const year = 2021
const yAxisData = ['项目一', '项目二', '项目三', '项目四', '项目五', '项目六', '项目七']

// 原始数据
const yearData = ref({
  planStartTime: ['2021-01-05', '2021-02-01', '2021-03-05', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25'],
  planEndTime: ['2021-01-05', '2021-02-08', '2021-03-14', '2021-04-01', '2021-04-10', '2021-05-25', '2021-07-25'],
  actualStartTime: ['2021-01-05', '2021-02-01', '2021-03-05', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25'],
  actualEndTime: ['2021-01-05', '2021-02-08', '2021-03-14', '2021-04-01', '2021-04-10', '2021-05-25', '2021-07-25'],
})

function getMonthTicks(year = 2021) {
  return Array.from({ length: 12 }, (_, i) => `${year}-${String(i + 1).padStart(2, '0')}`)
}
function getDayTicks(year = 2021, month = 1) {
  const days = new Date(year, month, 0).getDate()
  return Array.from(
    { length: days },
    (_, i) => `${year}-${String(month).padStart(2, '0')}-${String(i + 1).padStart(2, '0')}`,
  )
}

// 处理数据为custom series格式，保证数据与x轴一一对应
function getGanttSeriesData(type = 'plan', xData) {
  const startArr = yearData.value[type + 'StartTime']
  const endArr = yearData.value[type + 'EndTime']
  return yAxisData
    .map((name, i) => {
      let start = startArr[i]
      let end = endArr[i]
      let xStart, xEnd
      if (isYear.value) {
        // 年度视图，跨月任务横跨多格，单月任务只占一格
        const startMonth = start.slice(0, 7)
        const endMonth = end.slice(0, 7)
        xStart = xData.indexOf(startMonth)
        xEnd = xData.indexOf(endMonth)
        // 如果起止同月，条形只占1格
        if (xStart === xEnd) xEnd = xStart
      } else {
        // 月度视图，条形只显示该月内有任务的天数
        const monthStr = `${year}-${String(currentMonth.value).padStart(2, '0')}`
        const firstDay = `${monthStr}-01`
        const lastDay = xData[xData.length - 1]
        start = start.slice(0, 10)
        end = end.slice(0, 10)
        if (end < firstDay || start > lastDay) return null
        if (start < firstDay) start = firstDay
        if (end > lastDay) end = lastDay
        xStart = xData.indexOf(start)
        xEnd = xData.indexOf(end)
      }
      if (xStart === -1 || xEnd === -1) return null
      return {
        name,
        value: [xStart, i, xEnd - xStart + 1, startArr[i], endArr[i]],
      }
    })
    .filter(Boolean)
}

function formatDate(dateStr) {
  if (!dateStr) return '-'
  const [y, m, d] = dateStr.split('-')
  if (!y || !m || !d) return '-'
  return `${y}年${m}月${d}日`
}

function getOption() {
  let xData = []
  if (isYear.value) {
    xData = getMonthTicks(year)
  } else {
    xData = getDayTicks(year, currentMonth.value)
  }
  return {
    tooltip: {
      trigger: 'item',
      formatter(params) {
        const isPlan = params.seriesName === '计划时间'
        let start = formatDate(params.data[3])
        let end = formatDate(params.data[4])
        return (
          `<div>项目：${yAxisData[params.value[1]]}</div>` +
          `<div>${isPlan ? '计划进度' : '实际进度'}</div>` +
          `<div>开始：${start}</div>` +
          `<div>结束：${end}</div>`
        )
      },
    },
    grid: {
      containLabel: true,
      right: 80,
      left: 40,
      bottom: 40,
      top: 20,
      backgroundColor: '#fff',
    },
    legend: {
      data: ['计划时间', '实际完成'],
      align: 'auto',
      top: 'bottom',
    },
    xAxis: {
      type: 'category',
      data: xData,
      position: 'top',
      axisLabel: {
        formatter: (val) => {
          if (isYear.value) return val.split('-')[1] + '月'
          return val.split('-')[2] + '日'
        },
      },
      triggerEvent: true,
    },
    yAxis: {
      type: 'category',
      inverse: true,
      data: yAxisData,
      axisTick: { show: false },
      axisLine: { show: false },
    },
    series: [
      {
        name: '计划时间',
        type: 'custom',
        z: 3,
        renderItem: function (params, api) {
          const categoryIndex = api.value(1)
          const start = api.coord([api.value(0), categoryIndex])
          const end = api.coord([api.value(0) + api.value(2), categoryIndex])
          const height = api.size([0, 1])[1] * 0.6
          return {
            type: 'rect',
            shape: {
              x: start[0],
              y: start[1] - height / 2 + height / 2,
              width: end[0] - start[0],
              height: height / 2,
            },
            style: { fill: '#7ED6F9' },
          }
        },
        itemStyle: { color: '#7ED6F9' },
        encode: { x: 0, y: 1 },
        data: getGanttSeriesData('plan', xData),
      },
      {
        name: '实际完成',
        type: 'custom',
        z: 2,
        renderItem: function (params, api) {
          const categoryIndex = api.value(1)
          const start = api.coord([api.value(0), categoryIndex])
          const end = api.coord([api.value(0) + api.value(2), categoryIndex])
          const height = api.size([0, 1])[1] * 0.6
          return {
            type: 'rect',
            shape: {
              x: start[0],
              y: start[1] - height / 2,
              width: end[0] - start[0],
              height: height / 2,
            },
            style: { fill: '#FFC04D' },
          }
        },
        itemStyle: { color: '#FFC04D' },
        encode: { x: 0, y: 1 },
        data: getGanttSeriesData('actual', xData),
      },
    ],
  }
}

function initChart() {
  if (!ChartRef.value) return
  progressChart?.dispose?.()
  progressChart = echarts.init(ChartRef.value)
  const option = getOption()
  progressChart.setOption(option)
  window.addEventListener('resize', () => {
    progressChart.resize()
  })
  progressChart.on('click', function (params) {
    if (params.componentType === 'xAxis') {
      if (isYear.value) {
        // 切换到月视图
        const monthStr = params.value // '2021-03'
        currentMonth.value = Number(monthStr.split('-')[1])
        isYear.value = false
      } else {
        // 切回年视图
        isYear.value = true
      }
    }
  })
}

watch([isYear, currentMonth], () => {
  initChart()
})

onMounted(() => {
  initChart()
})
</script>

<style scoped>
#app {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}
.progressChart {
  width: 80%;
  height: 420px;
}
</style>
